home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / PEAR / Common.php < prev    next >
Encoding:
PHP Script  |  2005-12-02  |  36.0 KB  |  1,129 lines

  1. <?php
  2. /**
  3.  * PEAR_Common, the base class for the PEAR Installer
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category   pear
  14.  * @package    PEAR
  15.  * @author     Stig Bakken <ssb@php.net>
  16.  * @author     Tomas V. V. Cox <cox@idecnet.com>
  17.  * @author     Greg Beaver <cellog@php.net>
  18.  * @copyright  1997-2005 The PHP Group
  19.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  20.  * @version    CVS: $Id: Common.php,v 1.152 2005/11/12 02:26:59 cellog Exp $
  21.  * @link       http://pear.php.net/package/PEAR
  22.  * @since      File available since Release 0.1.0
  23.  * @deprecated File deprecated since Release 1.4.0a1
  24.  */
  25.  
  26. /**
  27.  * Include error handling
  28.  */
  29. require_once 'PEAR.php';
  30.  
  31. // {{{ constants and globals
  32.  
  33. /**
  34.  * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
  35.  */
  36. define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
  37. define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
  38. define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '$/');
  39.  
  40. // this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
  41. define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
  42. define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '$/i');
  43.  
  44. // XXX far from perfect :-)
  45. define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
  46.     ')(-([.0-9a-zA-Z]+))?');
  47. define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
  48.     '$/');
  49.  
  50. define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9_\.]+');
  51. define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '$/');
  52.  
  53. // this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
  54. define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(\/[a-zA-Z0-9-]+)*');
  55. define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '$/i');
  56.  
  57. define('_PEAR_CHANNELS_PACKAGE_PREG',  '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
  58.          . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
  59. define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '$/i');
  60.  
  61. define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
  62.     . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
  63. define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '$/');
  64.  
  65. /**
  66.  * List of temporary files and directories registered by
  67.  * PEAR_Common::addTempFile().
  68.  * @var array
  69.  */
  70. $GLOBALS['_PEAR_Common_tempfiles'] = array();
  71.  
  72. /**
  73.  * Valid maintainer roles
  74.  * @var array
  75.  */
  76. $GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
  77.  
  78. /**
  79.  * Valid release states
  80.  * @var array
  81.  */
  82. $GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
  83.  
  84. /**
  85.  * Valid dependency types
  86.  * @var array
  87.  */
  88. $GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
  89.  
  90. /**
  91.  * Valid dependency relations
  92.  * @var array
  93.  */
  94. $GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
  95.  
  96. /**
  97.  * Valid file roles
  98.  * @var array
  99.  */
  100. $GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
  101.  
  102. /**
  103.  * Valid replacement types
  104.  * @var array
  105.  */
  106. $GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
  107.  
  108. /**
  109.  * Valid "provide" types
  110.  * @var array
  111.  */
  112. $GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
  113.  
  114. /**
  115.  * Valid "provide" types
  116.  * @var array
  117.  */
  118. $GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
  119.  
  120. // }}}
  121.  
  122. /**
  123.  * Class providing common functionality for PEAR administration classes.
  124.  * @category   pear
  125.  * @package    PEAR
  126.  * @author     Stig Bakken <ssb@php.net>
  127.  * @author     Tomas V. V. Cox <cox@idecnet.com>
  128.  * @author     Greg Beaver <cellog@php.net>
  129.  * @copyright  1997-2005 The PHP Group
  130.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  131.  * @version    Release: 1.4.5
  132.  * @link       http://pear.php.net/package/PEAR
  133.  * @since      Class available since Release 1.4.0a1
  134.  * @deprecated This class will disappear, and its components will be spread
  135.  *             into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
  136.  */
  137. class PEAR_Common extends PEAR
  138. {
  139.     // {{{ properties
  140.  
  141.     /** stack of elements, gives some sort of XML context */
  142.     var $element_stack = array();
  143.  
  144.     /** name of currently parsed XML element */
  145.     var $current_element;
  146.  
  147.     /** array of attributes of the currently parsed XML element */
  148.     var $current_attributes = array();
  149.  
  150.     /** assoc with information about a package */
  151.     var $pkginfo = array();
  152.  
  153.     /**
  154.      * User Interface object (PEAR_Frontend_* class).  If null,
  155.      * the log() method uses print.
  156.      * @var object
  157.      */
  158.     var $ui = null;
  159.  
  160.     /**
  161.      * Configuration object (PEAR_Config).
  162.      * @var object
  163.      */
  164.     var $config = null;
  165.  
  166.     var $current_path = null;
  167.  
  168.     /**
  169.      * PEAR_SourceAnalyzer instance
  170.      * @var object
  171.      */
  172.     var $source_analyzer = null;
  173.     /**
  174.      * Flag variable used to mark a valid package file
  175.      * @var boolean
  176.      * @access private
  177.      */
  178.     var $_validPackageFile;
  179.  
  180.     // }}}
  181.  
  182.     // {{{ constructor
  183.  
  184.     /**
  185.      * PEAR_Common constructor
  186.      *
  187.      * @access public
  188.      */
  189.     function PEAR_Common()
  190.     {
  191.         parent::PEAR();
  192.         $this->config = &PEAR_Config::singleton();
  193.         $this->debug = $this->config->get('verbose');
  194.     }
  195.  
  196.     // }}}
  197.     // {{{ destructor
  198.  
  199.     /**
  200.      * PEAR_Common destructor
  201.      *
  202.      * @access private
  203.      */
  204.     function _PEAR_Common()
  205.     {
  206.         // doesn't work due to bug #14744
  207.         //$tempfiles = $this->_tempfiles;
  208.         $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
  209.         while ($file = array_shift($tempfiles)) {
  210.             if (@is_dir($file)) {
  211.                 if (!class_exists('System')) {
  212.                     require_once 'System.php';
  213.                 }
  214.                 System::rm(array('-rf', $file));
  215.             } elseif (file_exists($file)) {
  216.                 unlink($file);
  217.             }
  218.         }
  219.     }
  220.  
  221.     // }}}
  222.     // {{{ addTempFile()
  223.  
  224.     /**
  225.      * Register a temporary file or directory.  When the destructor is
  226.      * executed, all registered temporary files and directories are
  227.      * removed.
  228.      *
  229.      * @param string  $file  name of file or directory
  230.      *
  231.      * @return void
  232.      *
  233.      * @access public
  234.      */
  235.     function addTempFile($file)
  236.     {
  237.         if (!class_exists('PEAR_Frontend')) {
  238.             require_once 'PEAR/Frontend.php';
  239.         }
  240.         PEAR_Frontend::addTempFile($file);
  241.     }
  242.  
  243.     // }}}
  244.     // {{{ mkDirHier()
  245.  
  246.     /**
  247.      * Wrapper to System::mkDir(), creates a directory as well as
  248.      * any necessary parent directories.
  249.      *
  250.      * @param string  $dir  directory name
  251.      *
  252.      * @return bool TRUE on success, or a PEAR error
  253.      *
  254.      * @access public
  255.      */
  256.     function mkDirHier($dir)
  257.     {
  258.         $this->log(2, "+ create dir $dir");
  259.         if (!class_exists('System')) {
  260.             require_once 'System.php';
  261.         }
  262.         return System::mkDir(array('-p', $dir));
  263.     }
  264.  
  265.     // }}}
  266.     // {{{ log()
  267.  
  268.     /**
  269.      * Logging method.
  270.      *
  271.      * @param int    $level  log level (0 is quiet, higher is noisier)
  272.      * @param string $msg    message to write to the log
  273.      *
  274.      * @return void
  275.      *
  276.      * @access public
  277.      * @static
  278.      */
  279.     function log($level, $msg, $append_crlf = true)
  280.     {
  281.         if ($this->debug >= $level) {
  282.             if (!class_exists('PEAR_Frontend')) {
  283.                 require_once 'PEAR/Frontend.php';
  284.             }
  285.             $ui = &PEAR_Frontend::singleton();
  286.             if (is_a($ui, 'PEAR_Frontend')) {
  287.                 $ui->log($msg, $append_crlf);
  288.             } else {
  289.                 print "$msg\n";
  290.             }
  291.         }
  292.     }
  293.  
  294.     // }}}
  295.     // {{{ mkTempDir()
  296.  
  297.     /**
  298.      * Create and register a temporary directory.
  299.      *
  300.      * @param string $tmpdir (optional) Directory to use as tmpdir.
  301.      *                       Will use system defaults (for example
  302.      *                       /tmp or c:\windows\temp) if not specified
  303.      *
  304.      * @return string name of created directory
  305.      *
  306.      * @access public
  307.      */
  308.     function mkTempDir($tmpdir = '')
  309.     {
  310.         if ($tmpdir) {
  311.             $topt = array('-t', $tmpdir);
  312.         } else {
  313.             $topt = array();
  314.         }
  315.         $topt = array_merge($topt, array('-d', 'pear'));
  316.         if (!class_exists('System')) {
  317.             require_once 'System.php';
  318.         }
  319.         if (!$tmpdir = System::mktemp($topt)) {
  320.             return false;
  321.         }
  322.         $this->addTempFile($tmpdir);
  323.         return $tmpdir;
  324.     }
  325.  
  326.     // }}}
  327.     // {{{ setFrontendObject()
  328.  
  329.     /**
  330.      * Set object that represents the frontend to be used.
  331.      *
  332.      * @param  object Reference of the frontend object
  333.      * @return void
  334.      * @access public
  335.      */
  336.     function setFrontendObject(&$ui)
  337.     {
  338.         $this->ui = &$ui;
  339.     }
  340.  
  341.     // }}}
  342.  
  343.     // {{{ infoFromTgzFile()
  344.  
  345.     /**
  346.      * Returns information about a package file.  Expects the name of
  347.      * a gzipped tar file as input.
  348.      *
  349.      * @param string  $file  name of .tgz file
  350.      *
  351.      * @return array  array with package information
  352.      *
  353.      * @access public
  354.      * @deprecated use PEAR_PackageFile->fromTgzFile() instead
  355.      *
  356.      */
  357.     function infoFromTgzFile($file)
  358.     {
  359.         $packagefile = &new PEAR_PackageFile($this->config);
  360.         $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
  361.         if (PEAR::isError($pf)) {
  362.             $errs = $pf->getUserinfo();
  363.             if (is_array($errs)) {
  364.                 foreach ($errs as $error) {
  365.                     $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
  366.                 }
  367.             }
  368.             return $pf;
  369.         }
  370.         return $this->_postProcessValidPackagexml($pf);
  371.     }
  372.  
  373.     // }}}
  374.     // {{{ infoFromDescriptionFile()
  375.  
  376.     /**
  377.      * Returns information about a package file.  Expects the name of
  378.      * a package xml file as input.
  379.      *
  380.      * @param string  $descfile  name of package xml file
  381.      *
  382.      * @return array  array with package information
  383.      *
  384.      * @access public
  385.      * @deprecated use PEAR_PackageFile->fromPackageFile() instead
  386.      *
  387.      */
  388.     function infoFromDescriptionFile($descfile)
  389.     {
  390.         $packagefile = &new PEAR_PackageFile($this->config);
  391.         $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  392.         if (PEAR::isError($pf)) {
  393.             $errs = $pf->getUserinfo();
  394.             if (is_array($errs)) {
  395.                 foreach ($errs as $error) {
  396.                     $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
  397.                 }
  398.             }
  399.             return $pf;
  400.         }
  401.         return $this->_postProcessValidPackagexml($pf);
  402.     }
  403.  
  404.     // }}}
  405.     // {{{ infoFromString()
  406.  
  407.     /**
  408.      * Returns information about a package file.  Expects the contents
  409.      * of a package xml file as input.
  410.      *
  411.      * @param string  $data  contents of package.xml file
  412.      *
  413.      * @return array   array with package information
  414.      *
  415.      * @access public
  416.      * @deprecated use PEAR_PackageFile->fromXmlstring() instead
  417.      *
  418.      */
  419.     function infoFromString($data)
  420.     {
  421.         $packagefile = &new PEAR_PackageFile($this->config);
  422.         $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
  423.         if (PEAR::isError($pf)) {
  424.             $errs = $pf->getUserinfo();
  425.             if (is_array($errs)) {
  426.                 foreach ($errs as $error) {
  427.                     $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
  428.                 }
  429.             }
  430.             return $pf;
  431.         }
  432.         return $this->_postProcessValidPackagexml($pf);
  433.     }
  434.     // }}}
  435.  
  436.     /**
  437.      * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  438.      * @return array
  439.      */
  440.     function _postProcessValidPackagexml(&$pf)
  441.     {
  442.         if (is_a($pf, 'PEAR_PackageFile_v2')) {
  443.             // sort of make this into a package.xml 1.0-style array
  444.             // changelog is not converted to old format.
  445.             $arr = $pf->toArray(true);
  446.             $arr = array_merge($arr, $arr['old']);
  447.             unset($arr['old']);
  448.             unset($arr['xsdversion']);
  449.             unset($arr['contents']);
  450.             unset($arr['compatible']);
  451.             unset($arr['channel']);
  452.             unset($arr['uri']);
  453.             unset($arr['dependencies']);
  454.             unset($arr['phprelease']);
  455.             unset($arr['extsrcrelease']);
  456.             unset($arr['extbinrelease']);
  457.             unset($arr['bundle']);
  458.             unset($arr['lead']);
  459.             unset($arr['developer']);
  460.             unset($arr['helper']);
  461.             unset($arr['contributor']);
  462.             $arr['filelist'] = $pf->getFilelist();
  463.             $this->pkginfo = $arr;
  464.             return $arr;
  465.         } else {
  466.             $this->pkginfo = $pf->toArray();
  467.             return $this->pkginfo;
  468.         }
  469.     }
  470.     // {{{ infoFromAny()
  471.  
  472.     /**
  473.      * Returns package information from different sources
  474.      *
  475.      * This method is able to extract information about a package
  476.      * from a .tgz archive or from a XML package definition file.
  477.      *
  478.      * @access public
  479.      * @param  string Filename of the source ('package.xml', '<package>.tgz')
  480.      * @return string
  481.      * @deprecated use PEAR_PackageFile->fromAnyFile() instead
  482.      */
  483.     function infoFromAny($info)
  484.     {
  485.         if (is_string($info) && file_exists($info)) {
  486.             $packagefile = &new PEAR_PackageFile($this->config);
  487.             $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
  488.             if (PEAR::isError($pf)) {
  489.                 $errs = $pf->getUserinfo();
  490.                 if (is_array($errs)) {
  491.                     foreach ($errs as $error) {
  492.                         $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
  493.                     }
  494.                 }
  495.                 return $pf;
  496.             }
  497.             return $this->_postProcessValidPackagexml($pf);
  498.         }
  499.         return $info;
  500.     }
  501.  
  502.     // }}}
  503.     // {{{ xmlFromInfo()
  504.  
  505.     /**
  506.      * Return an XML document based on the package info (as returned
  507.      * by the PEAR_Common::infoFrom* methods).
  508.      *
  509.      * @param array  $pkginfo  package info
  510.      *
  511.      * @return string XML data
  512.      *
  513.      * @access public
  514.      * @deprecated use a PEAR_PackageFile_v* object's generator instead
  515.      */
  516.     function xmlFromInfo($pkginfo)
  517.     {
  518.         $config = &PEAR_Config::singleton();
  519.         $packagefile = &new PEAR_PackageFile($config);
  520.         $pf = &$packagefile->fromArray($pkginfo);
  521.         $gen = &$pf->getDefaultGenerator();
  522.         return $gen->toXml(PEAR_VALIDATE_PACKAGING);
  523.     }
  524.  
  525.     // }}}
  526.     // {{{ validatePackageInfo()
  527.  
  528.     /**
  529.      * Validate XML package definition file.
  530.      *
  531.      * @param  string $info Filename of the package archive or of the
  532.      *                package definition file
  533.      * @param  array $errors Array that will contain the errors
  534.      * @param  array $warnings Array that will contain the warnings
  535.      * @param  string $dir_prefix (optional) directory where source files
  536.      *                may be found, or empty if they are not available
  537.      * @access public
  538.      * @return boolean
  539.      * @deprecated use the validation of PEAR_PackageFile objects
  540.      */
  541.     function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
  542.     {
  543.         $config = &PEAR_Config::singleton();
  544.         $packagefile = &new PEAR_PackageFile($config);
  545.         PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  546.         if (strpos($info, '<?xml') !== false) {
  547.             $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
  548.         } else {
  549.             $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
  550.         }
  551.         PEAR::staticPopErrorHandling();
  552.         if (PEAR::isError($pf)) {
  553.             $errs = $pf->getUserinfo();
  554.             if (is_array($errs)) {
  555.                 foreach ($errs as $error) {
  556.                     if ($error['level'] == 'error') {
  557.                         $errors[] = $error['message'];
  558.                     } else {
  559.                         $warnings[] = $error['message'];
  560.                     }
  561.                 }
  562.             }
  563.             return false;
  564.         }
  565.         return true;
  566.     }
  567.  
  568.     // }}}
  569.     // {{{ buildProvidesArray()
  570.  
  571.     /**
  572.      * Build a "provides" array from data returned by
  573.      * analyzeSourceCode().  The format of the built array is like
  574.      * this:
  575.      *
  576.      *  array(
  577.      *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
  578.      *    ...
  579.      *  )
  580.      *
  581.      *
  582.      * @param array $srcinfo array with information about a source file
  583.      * as returned by the analyzeSourceCode() method.
  584.      *
  585.      * @return void
  586.      *
  587.      * @access public
  588.      *
  589.      */
  590.     function buildProvidesArray($srcinfo)
  591.     {
  592.         $file = basename($srcinfo['source_file']);
  593.         $pn = '';
  594.         if (isset($this->_packageName)) {
  595.             $pn = $this->_packageName;
  596.         }
  597.         $pnl = strlen($pn);
  598.         foreach ($srcinfo['declared_classes'] as $class) {
  599.             $key = "class;$class";
  600.             if (isset($this->pkginfo['provides'][$key])) {
  601.                 continue;
  602.             }
  603.             $this->pkginfo['provides'][$key] =
  604.                 array('file'=> $file, 'type' => 'class', 'name' => $class);
  605.             if (isset($srcinfo['inheritance'][$class])) {
  606.                 $this->pkginfo['provides'][$key]['extends'] =
  607.                     $srcinfo['inheritance'][$class];
  608.             }
  609.         }
  610.         foreach ($srcinfo['declared_methods'] as $class => $methods) {
  611.             foreach ($methods as $method) {
  612.                 $function = "$class::$method";
  613.                 $key = "function;$function";
  614.                 if ($method{0} == '_' || !strcasecmp($method, $class) ||
  615.                     isset($this->pkginfo['provides'][$key])) {
  616.                     continue;
  617.                 }
  618.                 $this->pkginfo['provides'][$key] =
  619.                     array('file'=> $file, 'type' => 'function', 'name' => $function);
  620.             }
  621.         }
  622.  
  623.         foreach ($srcinfo['declared_functions'] as $function) {
  624.             $key = "function;$function";
  625.             if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
  626.                 continue;
  627.             }
  628.             if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
  629.                 $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
  630.             }
  631.             $this->pkginfo['provides'][$key] =
  632.                 array('file'=> $file, 'type' => 'function', 'name' => $function);
  633.         }
  634.     }
  635.  
  636.     // }}}
  637.     // {{{ analyzeSourceCode()
  638.  
  639.     /**
  640.      * Analyze the source code of the given PHP file
  641.      *
  642.      * @param  string Filename of the PHP file
  643.      * @return mixed
  644.      * @access public
  645.      */
  646.     function analyzeSourceCode($file)
  647.     {
  648.         if (!function_exists("token_get_all")) {
  649.             return false;
  650.         }
  651.         if (!defined('T_DOC_COMMENT')) {
  652.             define('T_DOC_COMMENT', T_COMMENT);
  653.         }
  654.         if (!defined('T_INTERFACE')) {
  655.             define('T_INTERFACE', -1);
  656.         }
  657.         if (!defined('T_IMPLEMENTS')) {
  658.             define('T_IMPLEMENTS', -1);
  659.         }
  660.         if (!$fp = @fopen($file, "r")) {
  661.             return false;
  662.         }
  663.         if (function_exists('file_get_contents')) {
  664.             fclose($fp);
  665.             $contents = file_get_contents($file);
  666.         } else {
  667.             $contents = fread($fp, filesize($file));
  668.             fclose($fp);
  669.         }
  670.         $tokens = token_get_all($contents);
  671. /*
  672.         for ($i = 0; $i < sizeof($tokens); $i++) {
  673.             @list($token, $data) = $tokens[$i];
  674.             if (is_string($token)) {
  675.                 var_dump($token);
  676.             } else {
  677.                 print token_name($token) . ' ';
  678.                 var_dump(rtrim($data));
  679.             }
  680.         }
  681. */
  682.         $look_for = 0;
  683.         $paren_level = 0;
  684.         $bracket_level = 0;
  685.         $brace_level = 0;
  686.         $lastphpdoc = '';
  687.         $current_class = '';
  688.         $current_interface = '';
  689.         $current_class_level = -1;
  690.         $current_function = '';
  691.         $current_function_level = -1;
  692.         $declared_classes = array();
  693.         $declared_interfaces = array();
  694.         $declared_functions = array();
  695.         $declared_methods = array();
  696.         $used_classes = array();
  697.         $used_functions = array();
  698.         $extends = array();
  699.         $implements = array();
  700.         $nodeps = array();
  701.         $inquote = false;
  702.         $interface = false;
  703.         for ($i = 0; $i < sizeof($tokens); $i++) {
  704.             if (is_array($tokens[$i])) {
  705.                 list($token, $data) = $tokens[$i];
  706.             } else {
  707.                 $token = $tokens[$i];
  708.                 $data = '';
  709.             }
  710.             if ($inquote) {
  711.                 if ($token != '"') {
  712.                     continue;
  713.                 } else {
  714.                     $inquote = false;
  715.                     continue;
  716.                 }
  717.             }
  718.             switch ($token) {
  719.                 case T_WHITESPACE:
  720.                     continue;
  721.                 case ';':
  722.                     if ($interface) {
  723.                         $current_function = '';
  724.                         $current_function_level = -1;
  725.                     }
  726.                     break;
  727.                 case '"':
  728.                     $inquote = true;
  729.                     break;
  730.                 case T_CURLY_OPEN:
  731.                 case T_DOLLAR_OPEN_CURLY_BRACES:
  732.                 case '{': $brace_level++; continue 2;
  733.                 case '}':
  734.                     $brace_level--;
  735.                     if ($current_class_level == $brace_level) {
  736.                         $current_class = '';
  737.                         $current_class_level = -1;
  738.                     }
  739.                     if ($current_function_level == $brace_level) {
  740.                         $current_function = '';
  741.                         $current_function_level = -1;
  742.                     }
  743.                     continue 2;
  744.                 case '[': $bracket_level++; continue 2;
  745.                 case ']': $bracket_level--; continue 2;
  746.                 case '(': $paren_level++;   continue 2;
  747.                 case ')': $paren_level--;   continue 2;
  748.                 case T_INTERFACE:
  749.                     $interface = true;
  750.                 case T_CLASS:
  751.                     if (($current_class_level != -1) || ($current_function_level != -1)) {
  752.                         PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
  753.                             PEAR_COMMON_ERROR_INVALIDPHP);
  754.                         return false;
  755.                     }
  756.                 case T_FUNCTION:
  757.                 case T_NEW:
  758.                 case T_EXTENDS:
  759.                 case T_IMPLEMENTS:
  760.                     $look_for = $token;
  761.                     continue 2;
  762.                 case T_STRING:
  763.                     if (version_compare(zend_version(), '2.0', '<')) {
  764.                         if (in_array(strtolower($data),
  765.                             array('public', 'private', 'protected', 'abstract',
  766.                                   'interface', 'implements', 'throw') 
  767.                                  )) {
  768.                             PEAR::raiseError('Error: PHP5 token encountered in ' . $file . 
  769.                             'packaging should be done in PHP 5');
  770.                             return false;
  771.                         }
  772.                     }
  773.                     if ($look_for == T_CLASS) {
  774.                         $current_class = $data;
  775.                         $current_class_level = $brace_level;
  776.                         $declared_classes[] = $current_class;
  777.                     } elseif ($look_for == T_INTERFACE) {
  778.                         $current_interface = $data;
  779.                         $current_class_level = $brace_level;
  780.                         $declared_interfaces[] = $current_interface;
  781.                     } elseif ($look_for == T_IMPLEMENTS) {
  782.                         $implements[$current_class] = $data;
  783.                     } elseif ($look_for == T_EXTENDS) {
  784.                         $extends[$current_class] = $data;
  785.                     } elseif ($look_for == T_FUNCTION) {
  786.                         if ($current_class) {
  787.                             $current_function = "$current_class::$data";
  788.                             $declared_methods[$current_class][] = $data;
  789.                         } elseif ($current_interface) {
  790.                             $current_function = "$current_interface::$data";
  791.                             $declared_methods[$current_interface][] = $data;
  792.                         } else {
  793.                             $current_function = $data;
  794.                             $declared_functions[] = $current_function;
  795.                         }
  796.                         $current_function_level = $brace_level;
  797.                         $m = array();
  798.                     } elseif ($look_for == T_NEW) {
  799.                         $used_classes[$data] = true;
  800.                     }
  801.                     $look_for = 0;
  802.                     continue 2;
  803.                 case T_VARIABLE:
  804.                     $look_for = 0;
  805.                     continue 2;
  806.                 case T_DOC_COMMENT:
  807.                 case T_COMMENT:
  808.                     if (preg_match('!^/\*\*\s!', $data)) {
  809.                         $lastphpdoc = $data;
  810.                         if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
  811.                             $nodeps = array_merge($nodeps, $m[1]);
  812.                         }
  813.                     }
  814.                     continue 2;
  815.                 case T_DOUBLE_COLON:
  816.                     if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
  817.                         PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
  818.                             PEAR_COMMON_ERROR_INVALIDPHP);
  819.                         return false;
  820.                     }
  821.                     $class = $tokens[$i - 1][1];
  822.                     if (strtolower($class) != 'parent') {
  823.                         $used_classes[$class] = true;
  824.                     }
  825.                     continue 2;
  826.             }
  827.         }
  828.         return array(
  829.             "source_file" => $file,
  830.             "declared_classes" => $declared_classes,
  831.             "declared_interfaces" => $declared_interfaces,
  832.             "declared_methods" => $declared_methods,
  833.             "declared_functions" => $declared_functions,
  834.             "used_classes" => array_diff(array_keys($used_classes), $nodeps),
  835.             "inheritance" => $extends,
  836.             "implements" => $implements,
  837.             );
  838.     }
  839.  
  840.     // }}}
  841.     // {{{  betterStates()
  842.  
  843.     /**
  844.      * Return an array containing all of the states that are more stable than
  845.      * or equal to the passed in state
  846.      *
  847.      * @param string Release state
  848.      * @param boolean Determines whether to include $state in the list
  849.      * @return false|array False if $state is not a valid release state
  850.      */
  851.     function betterStates($state, $include = false)
  852.     {
  853.         static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
  854.         $i = array_search($state, $states);
  855.         if ($i === false) {
  856.             return false;
  857.         }
  858.         if ($include) {
  859.             $i--;
  860.         }
  861.         return array_slice($states, $i + 1);
  862.     }
  863.  
  864.     // }}}
  865.     // {{{ detectDependencies()
  866.  
  867.     function detectDependencies($any, $status_callback = null)
  868.     {
  869.         if (!function_exists("token_get_all")) {
  870.             return false;
  871.         }
  872.         if (PEAR::isError($info = $this->infoFromAny($any))) {
  873.             return $this->raiseError($info);
  874.         }
  875.         if (!is_array($info)) {
  876.             return false;
  877.         }
  878.         $deps = array();
  879.         $used_c = $decl_c = $decl_f = $decl_m = array();
  880.         foreach ($info['filelist'] as $file => $fa) {
  881.             $tmp = $this->analyzeSourceCode($file);
  882.             $used_c = @array_merge($used_c, $tmp['used_classes']);
  883.             $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
  884.             $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
  885.             $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
  886.             $inheri = @array_merge($inheri, $tmp['inheritance']);
  887.         }
  888.         $used_c = array_unique($used_c);
  889.         $decl_c = array_unique($decl_c);
  890.         $undecl_c = array_diff($used_c, $decl_c);
  891.         return array('used_classes' => $used_c,
  892.                      'declared_classes' => $decl_c,
  893.                      'declared_methods' => $decl_m,
  894.                      'declared_functions' => $decl_f,
  895.                      'undeclared_classes' => $undecl_c,
  896.                      'inheritance' => $inheri,
  897.                      );
  898.     }
  899.  
  900.     // }}}
  901.     // {{{ getUserRoles()
  902.  
  903.     /**
  904.      * Get the valid roles for a PEAR package maintainer
  905.      *
  906.      * @return array
  907.      * @static
  908.      */
  909.     function getUserRoles()
  910.     {
  911.         return $GLOBALS['_PEAR_Common_maintainer_roles'];
  912.     }
  913.  
  914.     // }}}
  915.     // {{{ getReleaseStates()
  916.  
  917.     /**
  918.      * Get the valid package release states of packages
  919.      *
  920.      * @return array
  921.      * @static
  922.      */
  923.     function getReleaseStates()
  924.     {
  925.         return $GLOBALS['_PEAR_Common_release_states'];
  926.     }
  927.  
  928.     // }}}
  929.     // {{{ getDependencyTypes()
  930.  
  931.     /**
  932.      * Get the implemented dependency types (php, ext, pkg etc.)
  933.      *
  934.      * @return array
  935.      * @static
  936.      */
  937.     function getDependencyTypes()
  938.     {
  939.         return $GLOBALS['_PEAR_Common_dependency_types'];
  940.     }
  941.  
  942.     // }}}
  943.     // {{{ getDependencyRelations()
  944.  
  945.     /**
  946.      * Get the implemented dependency relations (has, lt, ge etc.)
  947.      *
  948.      * @return array
  949.      * @static
  950.      */
  951.     function getDependencyRelations()
  952.     {
  953.         return $GLOBALS['_PEAR_Common_dependency_relations'];
  954.     }
  955.  
  956.     // }}}
  957.     // {{{ getFileRoles()
  958.  
  959.     /**
  960.      * Get the implemented file roles
  961.      *
  962.      * @return array
  963.      * @static
  964.      */
  965.     function getFileRoles()
  966.     {
  967.         return $GLOBALS['_PEAR_Common_file_roles'];
  968.     }
  969.  
  970.     // }}}
  971.     // {{{ getReplacementTypes()
  972.  
  973.     /**
  974.      * Get the implemented file replacement types in
  975.      *
  976.      * @return array
  977.      * @static
  978.      */
  979.     function getReplacementTypes()
  980.     {
  981.         return $GLOBALS['_PEAR_Common_replacement_types'];
  982.     }
  983.  
  984.     // }}}
  985.     // {{{ getProvideTypes()
  986.  
  987.     /**
  988.      * Get the implemented file replacement types in
  989.      *
  990.      * @return array
  991.      * @static
  992.      */
  993.     function getProvideTypes()
  994.     {
  995.         return $GLOBALS['_PEAR_Common_provide_types'];
  996.     }
  997.  
  998.     // }}}
  999.     // {{{ getScriptPhases()
  1000.  
  1001.     /**
  1002.      * Get the implemented file replacement types in
  1003.      *
  1004.      * @return array
  1005.      * @static
  1006.      */
  1007.     function getScriptPhases()
  1008.     {
  1009.         return $GLOBALS['_PEAR_Common_script_phases'];
  1010.     }
  1011.  
  1012.     // }}}
  1013.     // {{{ validPackageName()
  1014.  
  1015.     /**
  1016.      * Test whether a string contains a valid package name.
  1017.      *
  1018.      * @param string $name the package name to test
  1019.      *
  1020.      * @return bool
  1021.      *
  1022.      * @access public
  1023.      */
  1024.     function validPackageName($name)
  1025.     {
  1026.         return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
  1027.     }
  1028.  
  1029.  
  1030.     // }}}
  1031.     // {{{ validPackageVersion()
  1032.  
  1033.     /**
  1034.      * Test whether a string contains a valid package version.
  1035.      *
  1036.      * @param string $ver the package version to test
  1037.      *
  1038.      * @return bool
  1039.      *
  1040.      * @access public
  1041.      */
  1042.     function validPackageVersion($ver)
  1043.     {
  1044.         return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
  1045.     }
  1046.  
  1047.  
  1048.     // }}}
  1049.  
  1050.     // {{{ downloadHttp()
  1051.  
  1052.     /**
  1053.      * Download a file through HTTP.  Considers suggested file name in
  1054.      * Content-disposition: header and can run a callback function for
  1055.      * different events.  The callback will be called with two
  1056.      * parameters: the callback type, and parameters.  The implemented
  1057.      * callback types are:
  1058.      *
  1059.      *  'setup'       called at the very beginning, parameter is a UI object
  1060.      *                that should be used for all output
  1061.      *  'message'     the parameter is a string with an informational message
  1062.      *  'saveas'      may be used to save with a different file name, the
  1063.      *                parameter is the filename that is about to be used.
  1064.      *                If a 'saveas' callback returns a non-empty string,
  1065.      *                that file name will be used as the filename instead.
  1066.      *                Note that $save_dir will not be affected by this, only
  1067.      *                the basename of the file.
  1068.      *  'start'       download is starting, parameter is number of bytes
  1069.      *                that are expected, or -1 if unknown
  1070.      *  'bytesread'   parameter is the number of bytes read so far
  1071.      *  'done'        download is complete, parameter is the total number
  1072.      *                of bytes read
  1073.      *  'connfailed'  if the TCP connection fails, this callback is called
  1074.      *                with array(host,port,errno,errmsg)
  1075.      *  'writefailed' if writing to disk fails, this callback is called
  1076.      *                with array(destfile,errmsg)
  1077.      *
  1078.      * If an HTTP proxy has been configured (http_proxy PEAR_Config
  1079.      * setting), the proxy will be used.
  1080.      *
  1081.      * @param string  $url       the URL to download
  1082.      * @param object  $ui        PEAR_Frontend_* instance
  1083.      * @param object  $config    PEAR_Config instance
  1084.      * @param string  $save_dir  (optional) directory to save file in
  1085.      * @param mixed   $callback  (optional) function/method to call for status
  1086.      *                           updates
  1087.      *
  1088.      * @return string  Returns the full path of the downloaded file or a PEAR
  1089.      *                 error on failure.  If the error is caused by
  1090.      *                 socket-related errors, the error object will
  1091.      *                 have the fsockopen error code available through
  1092.      *                 getCode().
  1093.      *
  1094.      * @access public
  1095.      * @deprecated in favor of PEAR_Downloader::downloadHttp()
  1096.      */
  1097.     function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
  1098.     {
  1099.         if (!class_exists('PEAR_Downloader')) {
  1100.             require_once 'PEAR/Downloader.php';
  1101.         }
  1102.         return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback);
  1103.     }
  1104.  
  1105.     // }}}
  1106.  
  1107.     /**
  1108.      * @param string $path relative or absolute include path
  1109.      * @return boolean
  1110.      * @static
  1111.      */
  1112.     function isIncludeable($path)
  1113.     {
  1114.         if (file_exists($path) && is_readable($path)) {
  1115.             return true;
  1116.         }
  1117.         $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
  1118.         foreach ($ipath as $include) {
  1119.             $test = realpath($include . DIRECTORY_SEPARATOR . $path);
  1120.             if (file_exists($test) && is_readable($test)) {
  1121.                 return true;
  1122.             }
  1123.         }
  1124.         return false;
  1125.     }
  1126. }
  1127. require_once 'PEAR/Config.php';
  1128. require_once 'PEAR/PackageFile.php';
  1129. ?>